package top.haijunit.work.core.auth;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.SecretKey;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.Map;
import java.util.UUID;
import java.util.function.Function;

/**
 * 【说明类功能】
 *
 * @author zhanghaijun
 * @since 2024-08-22
 */
@Slf4j
@UtilityClass
public class JwtTokenUtils {

    // token的有效期，30分钟
    private static final Duration TOKEN_TERM = Duration.ofMinutes(30);
    // 刷新token的有效期，比token的有效期小6分钟，是为了程序性能考虑。
    private static final Duration REFRESH_TOKEN_TERM = Duration.ofMinutes(TOKEN_TERM.toMinutes() - 6);


    private SecretKey getSigningKey() {
        return Keys.hmacShaKeyFor(Decoders.BASE64.decode("asdfSFS34wfsdfsdfSDSD32dfsddDDerQSNCK34SOWEK5354fdgdf4"));
    }

    /**
     * 生成token
     *
     * @param extraClaims 额外信息
     * @param subject     用户名
     * @return token
     */
    public String generateToken(Map<String, Object> extraClaims, String subject) {
        Instant now = Instant.now();
        return Jwts.builder()
            .claims(extraClaims)
            .subject(subject)
            .id(UUID.randomUUID().toString())
            .issuedAt(Date.from(now))
            .expiration(Date.from(now.plus(TOKEN_TERM)))
            .signWith(getSigningKey()).compact();
    }

    /**
     * 解析token
     *
     * @param token token
     * @return 解析后的信息
     */
    public Claims extractAllClaims(String token) {
        return Jwts.parser().verifyWith(getSigningKey()).build().parseSignedClaims(token).getPayload();
    }

    private <T> T extractClaim(String token, Function<Claims, T> claimsResolvers) {
        return claimsResolvers.apply(extractAllClaims(token));
    }

    // 获取用户名
    public String extractUserName(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    // 获取过期时间
    private Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    // 判断是否过期
    public boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }

    // 刷新token
    public String refreshToken(String token) {
        Claims claims = extractAllClaims(token);
        Date expiration = claims.getExpiration();
        if (expiration.before(new Date())) {
            return null;
        }
        // 如果过期时间小于刷新时间，则生成新的token
        if (Duration.between(Instant.now(), Instant.ofEpochMilli(expiration.getTime())).compareTo(REFRESH_TOKEN_TERM) < 0) {
            return generateToken(claims, claims.getSubject());
        }
        return token;
    }
}
